/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */

#include <stdio.h>
#include <stdlib.h>
#include <aos/debug.h>
#include <aos/kernel.h>

#include "mvoice_impl.h"
#include "sampling.h"

#define TAG "VCPROC"

typedef struct {
    char *data;
    int size;
    int used_size;
} ai_data_chache_t;

#define EVT_ALG_STAT_QUIT   1

static mvoice_alg_t *g_ai_alg;
// static alg_data_format_t g_alg_data_format;
static int g_sample_num;
static void *ai_priv;
static ai_data_chache_t *data_cache;
static int ai_proc_running;
static int ai_proc_mute;
static aos_event_t evt_alg_state;
static int mute_nested = 0;

int mvoice_alg_register(mvoice_alg_t *alg)
{
    aos_check_return_einval(alg && alg->data_format.samples_per_frame);

    g_ai_alg = (mvoice_alg_t *)aos_zalloc_check(sizeof(mvoice_alg_t));
    *g_ai_alg = *alg;
    // g_alg_data_format = *input_format;

    // int size1 = input_format->samples_per_frame * 2;     //default 16 bits per sample
    // int size2 = g_alg_data_format.samples_per_frame * 2;

    /* find the apropriate cache size for reframing */
    // if (size1 != size2) {
    //     if (size1 > size2) {
    //         int tmp = size1;
    //         size1 = size2;
    //         size2 = tmp;
    //     }

    //     data_cache = (ai_data_chache_t *)aos_zalloc_check(sizeof(ai_data_chache_t));
    //     data_cache->size = (size2 / size1 + 1) * size1;
    //     data_cache->data = (char *)aos_malloc_check(data_cache->size);
    //     data_cache->used_size = 0;
    // }

    return 0;
}

// static int ai_cache_put(char *mic, char)

// static int ai_format_eq(alg_data_format_t *fmt1, alg_data_format_t *fmt2)
// {
//     if (fmt1->interleaved == fmt2->interleaved &&
//         fmt1->samples_per_frame == fmt2->samples_per_frame) {
//         return 1;
//     } else {
//         return 0;
//     }
// }

int mvoice_alg_init(mvoice_event_cb cb, void *priv)
{
    ai_priv = priv;

    aos_event_new(&evt_alg_state, 0);

	sample_init(g_ai_alg->data_format.sample_freq,
                g_ai_alg->data_format.samples_bits, 
                4 * g_ai_alg->data_format.samples_per_frame);
    return g_ai_alg->init(ai_priv, cb);
}

static int voice_kws_process(char *mic)
{
    aos_check_return_einval(mic && g_ai_alg);

    if (g_ai_alg->kws_proc) {
        g_ai_alg->kws_proc(ai_priv, mic);
        // if (ai_format_eq(&g_alg_data_format, &g_ai_alg->data_format)) {
        // } else {
        //     // TODO: 
        //     aos_assert(0);
        // }
    }

    return 0;
}

int voice_asr_process(char *mic)
{
    aos_check_return_einval(mic && g_ai_alg);

    if (g_ai_alg->asr_proc) {
        g_ai_alg->asr_proc(ai_priv, mic);
        // if (ai_format_eq(&g_alg_data_format, &g_ai_alg->data_format)) {
        //     g_ai_alg->asr_proc(ai_priv, mic, ref, out);
        // } else {
        //     // TODO: 
        //     aos_assert(0);
        // }
    }

    return 0;
}

// uint8_t playdata[50*1024];
// uint32_t playdata_len = 0;
void voice_process_tsk(void *arg)
{
    uint32_t frame_size = g_ai_alg->data_format.samples_per_frame * g_ai_alg->data_format.samples_bits / 8;
    uint32_t read_size;
    int ret = 0;
    uint8_t *frame_buf;

    frame_buf = (uint8_t *)aos_zalloc_check(frame_size);

    ai_proc_running = 1;

	while (ai_proc_running) {
		
	    read_size = sample_read(frame_buf, frame_size);
        if (read_size != frame_size) {
            LOGW(TAG, "sample read err return %u", read_size);
            continue;
        }

        // if (playdata_len + frame_size <= 50*1024) {
        //     memcpy(playdata + playdata_len, frame_buf, frame_size);
        //     playdata_len += frame_size;
        // }
        
        if (ai_proc_mute) {
            continue;
        }

        voice_kws_process(frame_buf);
        voice_asr_process(frame_buf);
	}

    aos_free(frame_buf);
    aos_event_set(&evt_alg_state, EVT_ALG_STAT_QUIT, AOS_EVENT_OR);
}

int mvoice_process_start(void)
{
    int ret;
    aos_task_t tsk_alg;

    if (!g_ai_alg) {
        LOGD(TAG, "register an ai algorithm first");
        return -1;
    }

	ret = sample_open();
    CHECK_RET_TAG_WITH_GOTO(ret == 0, END);

	ret = sample_start();
    CHECK_RET_TAG_WITH_GOTO(ret == 0, END);

    aos_task_new_ext(&tsk_alg, "algproc", voice_process_tsk, NULL, 1024 * 2, AOS_DEFAULT_APP_PRI);

    return 0;

END:
	sample_stop();
	sample_close(); 
    return ret;
}

int mvoice_process_stop(void)
{
    int flags = 0;

    if (ai_proc_running) {
        ai_proc_running = 0;
        aos_event_get(&evt_alg_state, EVT_ALG_STAT_QUIT, AOS_EVENT_OR_CLEAR, &flags, AOS_WAIT_FOREVER);

        sample_stop();
        sample_close();         
        g_ai_alg->deinit(ai_priv);
    }
    return 0;
}

int mvoice_process_pause(void)
{
    LOGD(TAG, "ai mute [%d]", mute_nested);

    if (!mute_nested++) {
        // LOGD(TAG, "ai muted");
        sample_stop();
        sample_close();
    }

    return 0;
}

int mvoice_process_resume(void)
{
    int ret;
    
    LOGD(TAG, "ai unmute [%d]", mute_nested);

    if (mute_nested) {
        if (!--mute_nested) {
            // LOGD(TAG, "ai unmuted");
            // ai_proc_mute = 0;
            ret = sample_open();
            aos_check_return_einval(ret == 0);

            ret = sample_start();
            aos_check_return_einval(ret == 0);
        }
    }

    return 0;
}